home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
EDUCATE
/
DIVECOMP.ARJ
/
DIVECOMP.SH
/
divecomp.c
< prev
next >
Wrap
Text File
|
1990-12-16
|
29KB
|
1,073 lines
/* $Header: divecomp.c,v 2.5 90/12/12 15:30:02 dave Exp $
$Log: divecomp.c,v $
* Revision 2.5 90/12/12 15:30:02 15:30:02 dave (Dave Waller)
* See file "rev_history" for information on new features.
*
* Revision 2.4 90/11/27 15:30:03 15:30:03 dave (Dave Waller)
* new features:
*
* - tissue loading graph now compresses when tissue loading exceeds
* 100%, to a scale of 0-200%. Rescales to 0-100% when all compartments
* are <= 100%.
*
* - Compartments can now be displayed as absolute pressure in (units) sea
* water, instead of % loading. Default is loading graph, absolute
* pressure can be selected at runtime with -P option. While the program
* is running, user can toggle between the two modes by typing 't' (this
* only works while calculations are taking place, not while waiting for
* input). Scale along the top is calibrated in (units) sea water absolute.
*
* - During calculation, depth can be adjusted up or down using the up and
* down arrow keys on the keyboard. For example, if the program is calculating
* a dive level of 60 feet for 50 minutes, the depth can be adjusted up or
* down by pressing the arrow keys. Adjustments are made in increments
* equal to the depth increment in the dive profile display.
*
* - If compartments are overloaded, the program can "autodecompress" by
* entering a depth of 'd' in interactive mode. The program will then
* move up to the ceiling value and "ride" it until all the compartments
* are <=100% loading.
*
* - When displaying compartment loading, pressure values are in (units) SWA
* instead of FSWA. (Of course, if the units are feet, then the values
* *are* FSWA).
*
* - Stats line during overloading now displays a more meaningful message
* regarding ceiling and decompression time.
*
* Revision 2.1 90/11/21 11:33:57 11:33:57 dave (Dave Waller)
* Changed SUN ifdefs to be macro SIMPLE instead... Makefile has been
* modified as well. Since Sun users can now compile with full SVID
* curses functionality, it seemed unnecessary to remove the reverse
* video bells and whistles for for them. However, this functionality
* is retained as a "simple" version of the program for those that do not
* have reverse video and underline capabilities on their terminals.
*
* Revision 2.0 90/11/21 11:11:30 11:11:30 dave (Dave Waller)
* Bug fixes:
*
* - incompatible typecasting in several places made the program fail
* with certain model files.
*
* - Logical error in the sample period determination algorithm.
*
* Enhancements:
*
* - added capability to display depth in arbitrary units. Default is
* feet. Units are specified in either a profile file on the first line
* or with the -u option. To use a different unit system, the user must
* supply the unit name and a unit conversion factor that represents
* units/ft. For example, to display in fathoms, the user would type
*
* divecomp -mEdge -ufathoms:0.1667
*
* There are 6 feet in a fathom, or 1/6=0.1667 fathoms per foot. For meters,
* the specification would be -umeters:0.3048, or -um:0.3, etc.
*
* The unit specification is written into an output profile, so that the
* correct units are used when using the profile. Units specified in a
* profile file override command line specification.
*
* - added logging capability. A log file can be specified with the -l
* option, and the program will write the allowed nitrogen %age for
* each compartment at each sample period into the log file. A header
* containing information regarding the model, sample period, and depth
* unit used is initially written into the file before the simulation
* begins. To make a log of a computer run, type
*
* divecomp -mBuhlman -umeters:0.3 -llog.buhlman
*
*
* Revision 1.9 90/11/20 11:24:48 11:24:48 dave (Dave Waller)
* Bug fixes:
* - logical errors in reading sample period from specified sources
* - update_dive_profile() routine hada rounding error in the
* code that compresses the display.
*
* Enhancements:
*
* - new profile file format. Profiles are now stored as depth-duration
* pairs, instead of discrete samples. This eliminates the need for
* sample period specification in the profile file, and makes the
* file more compact and easier to read. I have written a filter
* 'cvtprof' to convert from the old format to the new format, so
* any existing profiles that people have can be easily converted.
*
* Profiles created with the -o option in interactive mode are written
* in the new format.
*
* Revision 1.8 90/11/16 16:33:45 16:33:45 dave (Dave Waller)
* Cleaned up rounding algorithm for depth profile; added round up to
* compartment bar graph display, so that it reads 100% properly;
* Fixed scale graphic at top of compartment bar graph (it was off
* by one character position to the right, contributing tothe bars not
* being at 100% visually when they actually were).
*
* Revision 1.7 90/11/16 10:34:26 10:34:26 dave (Dave Waller)
* Added ability to specify different sample periods. Sample period ('s' in
* the source) is specified in the following manner (decreasing precedence):
*
* 1) via command line option
* Example: divecomp -mEdge -omyprof -s2.5
*
* Units are in minutes
*
* 2) From within a profile file. The first line can optionally
* specifiy a sample rate, if it has the form "s:<rate>".
* Example: A profile that contains 2 minute samples would
* look like
*
* s:2.0
* 60
* 60
* 60
* 60
* .
* .
* .
*
* (the ellipses [...] are not part of the file). 'divecomp'
* now automatically writes the sample period into every profile
* it generates via the -o option.
*
* 3) From a model file. Format is the same as a profile file.
* The most basic command, "divecomp -mEdge", for instance,
* will cause the computer to use the proper sampling rate
* for the Edge. Also, any profiles generates with this model
* will have the correct sampling value when used in different
* models.
*
* 4) Default: 1 minute.
*
* Revision 1.6 90/11/14 17:36:52 17:36:52 dave (Dave Waller)
* Added compile time checks to modify curses behavior to accomodate
* incomplete curses library on Sun/OS. Controlling tissue is highlighted
* with a preceding '*' instead of reverse video, deiling message does not
* appear in reverse video when tissue M values are exceeded, and tissue
* bar graph is composed of '#' characters instead of reverse video
* spaces. Not quite as pretty as the full curses version, but that's what
* you get if you're running on a Sun.
*
* Original graphical functionality is maintained for other platforms.
*
* Revision 1.5 90/11/14 13:58:43 13:58:43 dave (Dave Waller)
* Added the following features:
*
* - Decompression calculation and indication via ndl()
* - ingas/outgas indicator nxt to each compartment number
* - full "bottom timer" functions (i.e. dive #, bottom time, surface int)
*
* Fixed the following bugs:
*
* - would run forever if duration of 0 entered in interactive mode.
* Program now rejects such an entry, and asks for depth and duration
* again.
*
* - Controlling tissue indication lagged one sample behind actual value;
* Fixed.
*
* - Dive profile graph now rounds up to the nearest depth increment.
*
* Revision 1.4 90/11/13 16:38:49 16:38:49 dave (Dave Waller)
* Fixed up the comments.
*
* Revision 1.3 90/11/13 15:11:48 15:11:48 dave (Dave Waller)
* Initial checkin; started using RCS to keep track of revisions. This
* revision also fixes a bug with the code that reads in half times from model
* files -- they were stored as type 'int', which created problems for
* fscanf if the half times were floats. Changed the array 'half[]' to
* type 'float'.
* */
/* dive computer simulator, by Dave Waller (davew@hpdstma.hp.com)
========================================================================
Acknowlegments:
Much thanks to Eric Williams (sargon@portia.stanford.edu) for his
outstanding contribution to the ndl() routine to incorporate ceilings
and decompression times. His support and timely reviews/bug reports
have been extremely helpful. Thanks, Eric!
========================================================================
This program simulates a multi-compartment model dive computer, based
upon modified Haldanean tissue absoption models. The number of
compartments and their corresponding constants are not built into the
program, but must be loaded at runtime, allowing the program to
simulate most dive computers on the market today.
Subsequent derivation of equations as found in the comments below were
done by myself, as I sit here at work, using only my memory;
therefore, this initial pass at the simulation may contain some errors
that I will have to fix later, after I have had time to review the
pertinent material at home. Surprisingly enough, it seems to work
pretty accurately, based upon my experience with my ORCA Delpi. Have
fun!
Tissue halftimes in general express the time it takes for a
tissue to either absorb or release nitrogen such that the
tissue is 50% equalized to the pressure differential between
the ambient nitrogen pressure and the tissue nitrogen
pressure. This uptake or outgassing obeys an asymptotic
exponential, namely if ambient pressure is PA, and initial tissue
pressure is PT0, then the tissue pressure as a function of
time is expressed as,
PT = PT0 + (PA - PT0)(1 - e^(-kt))
where k = -ln(0.5)/T = 0.6931/T
half half
This formula holds true for static conditions; i.e. PA does
not change. In reality, a diver will probably be continuously
changing depth, and therefore PA is changing also. A
reasonable approximation of continous PT values can be
calculated by sampling PA at discrete intervals, and applying
the above formula over the preceding sample interval, then
starting over with a new PA and PT0 for the next interval
based upon the calculation.
In this simulated computer, we assume datapoints are recorded
one per minute. Since the halftimes are expressed in minutes,
the above formula can be reduce to an adjustment at each
sample that consists of the following:
PTnew = PTlast + K(PA - PTlast)
where K = (1 - e^(-k*1)) = 1-e^(-k) = 1 - (1/e^k) =
1 - (0.5)^(1/T )
half
Note that the K value is dependant upon consistency between
the units of the compartment half time and the computer
sample period. Specifically, the above formula only applies
if the sample period is 1 unit of the half time unit (in this
case minutes). For an arbitrary sample period s, the formula
for the K value is
K = 1 - (0.5)^(s/T )
half
The dive computer simulator calculates the K values on
startup, with the sample period 's' being taken from the
following sources, in order of decreasing priority:
1) a sample period specified on the command line with the
-s option
2) A sample period specified in a profile file. This must
take precedence over a sample period in a model file,
otherwise the number of samples in the file will not
match the dive profile in real time. In order to compare
different models, the same sampling period must be used
for a single profile.
3) A sample period specified in the model file
4) The default sample period of 1 second
*/
char scale100[]=" 10| 20| 30| 40| 50| 60| 70| 80| 90|100| % Allowed N2";
char scale200[]=" 20| 40| 60| 80|100|120|140|160|180|200| % Allowed N2";
#include <stdio.h>
#include <curses.h>
#define NCOMP 24
#define ONE_ATM_FSW 33.0
#define PN2 0.79
#define ONE_ATM_FSW_N2 PN2 * ONE_ATM_FSW
#define LN_HALF -0.6931
#define INF 32000 /* for ndl() */
#define DELTAM 1.0 /* temporary fix */
#define SHOW_LOADING 0
#define SHOW_FSWA 1
/* Some obscure variable names and their meanings:
bt Bottom Time
si Surface Interval
cf Compression factor (for compressing profile display)
di Depth Increment (for profile display)
*/
int cf=1, duration=0, samples=0, n=0, controlling_tissue, lines=24,
ceiling, dive=1, mode=SHOW_LOADING, logmode=SHOW_LOADING,
decomode=0, width=80;
float bt=0.0, si=0.0, apn2=ONE_ATM_FSW_N2, half[NCOMP], ucf=1.0, di,
depth, Kvalues[NCOMP], Mvalues[NCOMP], c[NCOMP], s=0.0,
depth_record[8192];
double ceil(), floor(), pow(), exp(), log();
float loading();
char buf[128], unitstr[16], scale100FSWA[80], scale200FSWA[80];
char revision[]="$Header: divecomp.c,v 2.5 90/12/12 15:30:02 dave Exp $";
FILE *profile=NULL, *outfile=NULL, *logfile=NULL;
main(argc,argv)
int argc;
char *argv[];
{
FILE *model=NULL, *fopen();
int i, opt;
char *getenv(), *envstr, *cptr, *strcpy(), *strchr(), mname[32];
char *strcat();
float get_sample(), dummy;
double atof();
extern char *optarg;
strcpy(unitstr,"ft");
/* find out how many lines display has */
if ((envstr=getenv("LINES"))!=0) lines=atoi(envstr);
if ((envstr=getenv("WIDTH"))!=0) width=atoi(envstr);
/* process command line options */
while ((opt=getopt(argc,argv,"p:m:o:s:u:l:P"))!=EOF) {
switch(opt) {
case 'p':
if ((profile=fopen(optarg,"r"))==NULL) {
perror(optarg);
exit(1);
}
break;
case 'm':
if ((model=fopen(optarg,"r"))==NULL) {
perror(optarg);
exit(1);
}
strcpy(mname,optarg);
break;
case 'o':
if ((outfile=fopen(optarg,"w"))==NULL) {
perror(optarg);
exit(1);
}
break;
case 's':
s = atof(optarg);
break;
case 'u':
cptr = strchr(optarg,(int)':');
*cptr = '\0';
ucf = atof(cptr+1);
strcpy(unitstr, optarg);
break;
case 'l':
if ((logfile=fopen(optarg,"w"))==NULL) {
perror(optarg);
exit(1);
}
break;
case 'P':
mode=logmode=SHOW_FSWA;
break;
}
}
if (argc == 1) {
fprintf(stderr, "Valid options are:\n");
fprintf(stderr, " -m<fname> Model file. REQUIRED.\n");
fprintf(stderr, " -p<fname> Input profile filename\n");
fprintf(stderr, " -o<fname> Output filename for dive profile\n");
fprintf(stderr, " -l<fname> Output logfile filename. logfile is depth and N2 vs time.\n");
fprintf(stderr, " -s<float> Sample period (in minutes)\n");
fprintf(stderr, " -u<name>:<float> Depth units. (new-unit)*<float> = 1 ft\n");
fprintf(stderr, " -P Display compartments as (units)SWA\n");
exit(0);
}
if (model==NULL) {
fprintf(stderr, "You must specify a model with the \"-m\" option\n");
exit(0);
}
/* check profile for depth unit specification */
if (profile)
if(fscanf(profile,"u:%[^:]:%f",unitstr,&ucf)==0)
rewind(profile);
/* check model file for sampling period */
if (fscanf(model,"s:%f",(s==0?&s:&dummy))==0) rewind(model);
if (s == 0.0) s = 1.0; /* default */
/* set up the pressure scales */
for (i=1; i<11; i++) {
sprintf(buf,"%3d|",(int)(i*10*ucf));
strcat(scale100FSWA,buf);
sprintf(buf,"%3d|",(int)(i*20*ucf));
strcat(scale200FSWA,buf);
}
sprintf(buf," %s sea water abs.", unitstr);
strcat(scale100FSWA,buf);
strcat(scale200FSWA,buf);
/* read in the computer model */
n = 0;
while(fscanf(model, "%f %f", &half[n], &Mvalues[n]) != EOF) {
Kvalues[n] = 1.0 - pow((double)0.5, (double)(s/half[n]));
n++;
}
fclose(model);
if (profile && outfile) {
fprintf(stderr,"-o option can only be used in interactive mode.\n");
exit(1);
}
/* put the depth unit into the outfile if there is one */
if (outfile) fprintf(outfile,"u:%s:%.4f\n",unitstr,ucf);
/* If logging, write the header into the logfile */
if (logfile) {
fprintf(logfile,"Model: %s Sample period: %f min\n\n",
mname, s);
if (logmode==SHOW_LOADING)
fprintf(logfile,"Depth\t%% Allowed Nitrogen\n(%s)\t",
unitstr);
else fprintf(logfile,"Depth\tCompartment pressures (%s sea water abs.)\n(%s)\t", unitstr, unitstr);
for (i=0; i<n; i++) fprintf(logfile,"c%02d ",i+1);
fprintf(logfile,"\n");
}
/* determine the depth increment used in the profile display */
for (di=ucf*5; ucf*200/di > lines-n-4; di+=ucf*5);
/* initialize tissue N2 levels */
for (i=0; i<NCOMP; i++) c[i] = ONE_ATM_FSW_N2;
/* initialize the display */
init_display();
update_display();
for (;;) {
get_sample();
/* update time values */
if (depth > 3) {
/* all depths >3ft are counted as bottom time */
if ((si > 0.0) && (si < 5.0)) {
/* We must account for brief periods of
surfacing during the dive. The surface
interval counter starts immediately upon
ascending to depths shallower than 3
feet, yet if the diver returns to depths
deeper than 3 feet within 5 minutes, the
time elapsed as surface interval must be
added into the bottom time value, and
the surface interval reset to 0 */
bt += si + s;
si = 0.0;
}
else if (si == 0.0) bt+=s; /* no intermediate
surface interval to
account for */
else if (si > 5.0) { /* else the diver has
been on the surface
for more than 5
minutes, so we treat
this as a new dive
*/
dive++;
bt=s;
si=0.0;
}
}
else si+=s; /* else the diver is still shallower than
3 feet; update the surface interval counter
*/
samples++;
/* update ambient N2 pressure */
apn2 = PN2*(depth+ONE_ATM_FSW);
/* update tissue (compartment) N2 levels */
update_tissues();
/* check if there has been a character input */
switch (getch()) {
case 't':
mode =
(mode==SHOW_LOADING?SHOW_FSWA:SHOW_LOADING);
break;
case 'q':
decomode = 0; /* reset vars that cause */
duration = 0; /* continuous calculation */
break;
case KEY_DOWN:
depth += di;
break;
case KEY_UP:
depth -= di;
break;
}
/* repaint the display */
update_display();
update_dive_profile();
refresh();
/* if logging, write entry */
if (logfile) {
fprintf(logfile,"%.1f\t", ucf*depth);
for (i=0; i<n; i++) {
if (logmode == SHOW_LOADING)
fprintf(logfile,"%-5d ",
(int)(100*loading(i)));
else fprintf(logfile,"%-5.1f ", c[i]);
}
fprintf(logfile,"\n");
}
}
}
update_display()
{
/* update the stats line first. Since the controlling tissue is
found in the ndl() routine which is called by the update_stats()
routine, it must be called before the tissue graph is repainted.
*/
update_stats();
update_ndl_limits();
if (mode == SHOW_LOADING) display_loading();
else display_fswa();
}
update_tissues()
{
int i;
for (i=0; i<n; i++) /* n == number of compartments */
c[i] = c[i] + (apn2 - c[i])*Kvalues[i];
}
update_stats()
{
int limit;
float time_to_surface();
char *showtime(), buf1[16], buf2[16], buf3[16];
move(n+2,15);
clrtoeol();
limit = ndl(apn2);
sprintf(buf, "Dive: %d Depth: %.1f %s BT: %s",
dive, ucf*depth, unitstr, showtime(buf1,bt));
addstr(buf);
move(n+3,15);
clrtoeol();
if (limit >= 0) {
sprintf(buf,"NDL: %s Sfc Int: %s",
showtime(buf2,(float)limit), showtime(buf3,si));
addstr(buf);
}
else {
attron(A_REVERSE);
sprintf(buf,"Ceiling: %.1f %s", ucf*ceiling,
unitstr);
addstr(buf);
sprintf(buf," Time to surface: %s",
showtime(buf1,time_to_surface()));
addstr(buf);
attroff(A_REVERSE);
}
}
update_dive_profile()
{
int i, j, x, y;
/* update the dive profile display */
if (samples/cf > 60) {
/* erase and compress display */
for (i=n+5; i<lines; i++) {
move(i,10);
clrtoeol();
}
cf *=2;
for (i=1; i<samples; i+=cf) {
x = 10+i/cf;
y = n+5+(int)ceil((double)ucf*depth_record[i]/di);
move((y>=lines?lines-1:y),x);
addch((y>=lines?'v':'*'));
}
}
x = 10+samples/cf;
y = n+5+ceil((double)ucf*depth/di);
move((y>=lines?lines-1:y),x);
addch((y>=lines?'v':'*'));
depth_record[samples] = depth;
}
update_ndl_limits()
{
int i;
int t;
float d;
float p;
char buf[10];
for (i=n+5; i<lines; i++) {
d = (i-n-5)*di/ucf; /* depth in feet */
p = 0.79 * (d + 33.0); /* pn2 at depth */
t = ndl(p); /* compute limits */
if (t<0) t=0;
move(i,0);
if (t >= INF) {
addstr("INF");
} else if (t >= 1000) {
addstr("***");
} else if (t>0) {
sprintf(buf,"%3d",t);
addstr(buf);
} else addstr(" ");
}
t = ndl(apn2); /* leave all the globals at the right values */
}
display_loading()
{
int i, j, k, temp, over;
/* if overpressure on any tissue, change the scale to 0 - 200% */
for (over=i=0; i<n; i++) if (c[i] > Mvalues[i]) over=1;
move(0,10);
clrtoeol();
if (over) addstr(scale200);
else addstr(scale100);
/* display tissue saturation */
for (i=0; i<n; i++) { /* n == number of compartments */
/* display the compartment number, highlighting if it
is the controlling tissue. */
#ifdef SIMPLE
move(1+i,6);
if (i==controlling_tissue) sprintf(buf,"*%d",i+1);
else sprintf(buf," %d",i+1);
#else
move(1+i,7);
if (i==controlling_tissue) attron(A_REVERSE);
sprintf(buf,"%d",i+1);
#endif
addstr(buf);
#ifndef SIMPLE
attroff(A_REVERSE);
#endif
/* display the ingas/outgas indicator for the
compartment. Ingas/outgas indicator is adjusted to 3 decimal
place accuracy. */
move(1+i,9);
temp = (apn2 - c[i]) * 1000.0;
if (temp > 0) addch('>');
if (temp == 0) addch(' ');
if (temp < 0) addch('<');
/* clear the bar before displaying the new value */
move(1+i,10);
clrtoeol();
k = ceil(floor((double)(1000.0 * loading(i))) * (over==0?0.04:0.02));
#ifndef SIMPLE
attron(A_REVERSE | A_UNDERLINE);
#endif
sprintf(buf," %4.2f",ucf*c[i]);
for (j=0; j<k; j++) {
if (j==(width-11-strlen(buf))) {
addch('>');
break;
}
#ifdef SIMPLE
if (j<(over==0?40:20)) addch('#');
#else
if (j<(over==0?40:20)) addch(' ');
#endif
else addch('*');
}
#ifndef SIMPLE
attroff(A_REVERSE | A_UNDERLINE);
#endif
addstr(buf);
}
}
display_fswa()
{
int i, j, k, temp, over, Mmarker;
/* if overpressure on any tissue, change the scale to 0 - 200% */
for (over=i=0; i<n; i++) if (c[i] > 100.0) over=1;
move(0,10);
clrtoeol();
if (over) addstr(scale200FSWA);
else addstr(scale100FSWA);
/* display compartment N2 levels in FSWA */
for (i=0; i<n; i++) { /* n == number of compartments */
/* display the compartment number, highlighting if it
is the controlling tissue. */
#ifdef SIMPLE
move(1+i,6);
if (i==controlling_tissue) sprintf(buf,"*%d",i+1);
else sprintf(buf," %d",i+1);
#else
move(1+i,7);
if (i==controlling_tissue) attron(A_REVERSE);
sprintf(buf,"%d",i+1);
#endif
addstr(buf);
#ifndef SIMPLE
attroff(A_REVERSE);
#endif
/* display the ingas/outgas indicator for the
compartment. Ingas/outgas indicator is adjusted to 3 decimal
place accuracy. */
move(1+i,9);
temp = (apn2 - c[i]) * 1000.0;
if (temp > 0) addch('>');
if (temp == 0) addch(' ');
if (temp < 0) addch('<');
/* clear the bar before displaying the new value */
move(1+i,10);
clrtoeol();
Mmarker = ceil((double)(Mvalues[i]*(over==0?0.4:0.2)));
k = ceil((double)(c[i]*(over==0?0.4:0.2)));
#ifndef SIMPLE
attron(A_REVERSE | A_UNDERLINE);
#endif
for (j=0; j<k; j++) {
if (j==(width-11)) {
addch('>');
break;
}
#ifdef SIMPLE
if (j<Mmarker) addch('#');
#else
if (j<Mmarker) addch(' ');
#endif
else addch('*');
}
#ifndef SIMPLE
attroff(A_REVERSE | A_UNDERLINE);
#endif
}
}
init_display()
{
int i;
initscr();
/* set nodelay mode for input processing */
nodelay(stdscr,TRUE);
keypad(stdscr,TRUE);
/* print the saturation %age scale */
move(0,10);
if (mode==SHOW_LOADING) addstr(scale100);
else addstr(scale100FSWA);
/* label the Dive profile graph */
move(n+3,0);
addstr("NDL Depth");
move(n+4,0);
addstr("----------");
/* create the dive profile display */
for (i=n+5; i<lines; i++) {
move(i,4);
sprintf(buf,"%5.1f|",(i-n-5)*di);
addstr(buf);
}
}
float get_sample()
{
float target_depth;
double atof();
while (duration == 0) {
if (!profile) {
/* check for autodecompression mode */
if (decomode) {
if (ceiling == 0) decomode = 0;
else {
target_depth =
ceil((double)ceiling/5.0)*5.0;
if (target_depth - depth > 30)
depth -= 30;
else depth = target_depth;
duration = 1;
}
continue;
}
/* read depth and duration from user */
nodelay(stdscr,FALSE);
move(n+4,15);
clrtoeol();
addstr("Enter Depth ('q' to quit): ");
refresh();
getstr(buf);
if (buf[0]=='q') {
nodelay(stdscr,FALSE);
endwin();
exit(0);
}
/* 'd' indicates autodecompression */
if (buf[0]=='d') {
decomode = 1;
move(n+4,15);
clrtoeol();
attron(A_REVERSE);
addstr("*** autodecompressing ***");
attroff(A_REVERSE);
continue;
}
depth = atof(buf);
move(n+4,15);
clrtoeol();
addstr("Enter Duration (min): ");
refresh();
getstr(buf);
duration = atof(buf) / s;
move(n+4,15);
clrtoeol();
if (outfile && duration)
fprintf(outfile,"%.1f %d\n",depth,
(int)(duration*s));
}
/* else get depth and duration from profile */
else if (fscanf(profile,"%f %d",&depth, &duration)==EOF) {
endwin();
exit(0);
}
else duration /= s;
depth /= ucf;
}
duration--;
nodelay(stdscr,TRUE);
return(depth);
}
float time_to_surface()
{
int i, j;
float time=0, ctemp[NCOMP], stop, next, pn2_stop, pn2_next,
target, temp, dt;
/* make a copy of the compartment N2 partial pressure values */
for (i=0; i<n; i++) ctemp[i] = c[i];
/* set up first stop data */
stop = ceil((double)(ceiling/5.0))*5.0;
pn2_stop = PN2*(stop+ONE_ATM_FSW);
/* for each stop, calculate the time to get to it from the
previous stop */
for (next=stop-5.0; next>=0.0; next-=5.0) {
pn2_next = PN2*(next+ONE_ATM_FSW);
for (i=0, temp=0; i<n; i++) {
/* determine the time to decompress compartment
to target N2 pressure. Target pressure is the
value at which the compartment yields a ceiling
equal to the next stop. Time is then calculated
using the exponential decay formula with the
current stop as the ambient pressure */
target = next * DELTAM + Mvalues[i];
if ((target < ctemp[i])&&(pn2_stop < ctemp[i])) {
dt = half[i] / LN_HALF *
log((double)(1.0 -
(target - ctemp[i]) /
(pn2_stop - ctemp[i])));
if (dt > temp) temp = dt;
}
}
/* convert dt to a number of samples */
dt = ceil((double)(temp/s));
/* adjust compartment values for next calculation */
for (i=0; i<n; i++)
for (j=0; j<dt; j++) ctemp[i] = ctemp[i] +
(pn2_stop - ctemp[i]) * Kvalues[i];
time += dt*s;
stop = next;
pn2_stop = PN2*(stop+ONE_ATM_FSW);
}
return(time);
}
ndl(pressure)
float pressure;
{
int i, limit, dlimit;
int dcontrol;
int safe, outgas, decom;
int t;
double time_fn();
limit = INF;
dlimit = 0;
dcontrol = -1;
ceiling = 0;
for (i=0; i<n; i++) {
safe = (pressure < Mvalues[i]);
outgas = (pressure < c[i]);
decom = (Mvalues[i] < c[i]);
if (decom) {
/* compute ceiling */
t = ceil((c[i] - Mvalues[i])/DELTAM);
if (t > ceiling) ceiling = t;
if ((outgas) && (safe)) {
/* how long to decompress */
t = - ceil(time_fn(pressure,i));
if (t < dlimit) {
dlimit = t;
dcontrol = i;
}
} else {
/* compartment will not decompress */
dlimit = -INF;
dcontrol = i;
}
} else if ((!safe) && (!outgas)) {
/* compute no-decompress time */
if (pressure == Mvalues[i]) {
t = INF;
} else if (pressure == c[i]) {
t = INF;
} else if (c[i] == Mvalues[i]) {
t = 0;
} else {
t = floor(time_fn(pressure,i));
}
if (t < limit) {
limit = t;
controlling_tissue = i;
}
}
}
/* if any tissues are over their m-value than they will control */
if (dcontrol >=0) {
controlling_tissue = dcontrol;
limit = dlimit;
}
return(limit);
}
char *showtime(tmpbuf,min)
char *tmpbuf;
float min;
{
char *strcpy();
if ((int)min >= INF) return(strcpy(tmpbuf,"INF"));
if ((int)min > 60) sprintf(tmpbuf,"%d h %d m", (int)min/60,
(int)min%60);
else sprintf(tmpbuf,"%d min", (int)min);
return(tmpbuf);
}
double time_fn(pressure, i)
float pressure;
int i;
{
double temp;
temp = (pressure-Mvalues[i])/(pressure-c[i]);
return(half[i]/LN_HALF * log(temp));
}
float loading(index)
int index;
{
return((c[index]-ONE_ATM_FSW_N2)/(Mvalues[index]-ONE_ATM_FSW_N2));
}